import json
from contextlib import contextmanager
from copy import copy

from common import constant
from common.documents import APIModel
from common.documents import Ask
from common.documents import SqlAsk, ParamGroupType
from common.state import detect_state
from common.tools import get_data_by_routes, get_data_by_route, get_data_route, parse


def summary_group(success_condition):  # 只支持select one
    def decorate(ite):
        success_data = set()
        fail_data = set()
        model = None
        for ask in ite:
            if not model:
                model = APIModel.objects(url=ask.url).first()
            if success_condition(ask):
                for route in get_data_route(ask.data):
                    success_data.add(tuple(route))
            else:
                for route in get_data_route(ask.data):
                    fail_data.add(tuple(route))
        goal = success_data - fail_data
        if goal:
            so_gp = ParamGroupType()
            so_gp.group_type = constant.GroupType.SELECT_ONE
            so_gp.save()
            for g in goal:
                yield g, so_gp.uid

    return decorate


def one_not_required(ask):
    model = APIModel.objects(url=ask.url).first()
    routes = model.args.get_require_route(True)
    addition_route = model.args.get_require_route(False)
    data = ask.data
    for only_route in addition_route:
        td = get_data_by_routes(data, [only_route] + routes, all_have=True)
        if td:
            tr = copy(ask)
            tr.data = td
            yield tr


def remove_not_required(ask):
    model = APIModel.objects(url=ask.url).first()
    data = ask.data
    for route in model.args.get_require_route(False):
        dt = data
        for r in route[:-1]:
            if r in dt:
                dt = dt[r]
            else:
                break
        else:
            if route[-1] in dt:
                del dt[route[-1]]
    tr = copy(ask)
    tr.data = data
    return tr


def remove_all_group(model):
    """
    删除所有的组
    :param model:模型
    :return:
    """
    for route in model.args.get_sub_route():
        arg = model.args.get_sub_arg(route)
        for group in arg.groups:
            group.delete()
        arg.groups = []
        arg.save()
    return model


def only_select(ask):
    model = APIModel.objects(url=ask.url).first()
    if model.only_selected():
        return True
    return False


def no_checked_param(ask, history):
    """过滤，只能通过没有在历史的route中出现的请求"""
    has_new = False
    for route in get_data_route(ask.data):
        if str(route) not in history:
            has_new = True
            history.append(str(route))
    return has_new


def update_required(ask):
    """
    修改模型中没有必要的参数上
    :param ask: 一次成功的请求
    :return:
    """
    model = APIModel.objects(url=ask.url).first()
    for route in model.args.get_require_route(True):  # 没有必要的参数
        try:
            get_data_by_route(ask.data, route)
        except:
            yield route


def remove_one_required(ask):
    """
    删除一个必要参数
    :return: 返回一个数组,里面是删除一个必要参数的列表
    """
    model = APIModel.objects(url=ask.url).first()
    routes = list(model.args.get_require_route(True))
    addition_route = list(model.args.get_require_route(False))
    data = ask.data
    for del_route in routes:
        gp = routes.copy()
        for i, rt in enumerate(gp):
            if del_route is rt:
                gp.pop(i)
                break
        td = get_data_by_routes(dict(data), gp + addition_route, all_have=False)
        solvers.py
        web_console.py
        tr = copy(ask)
        tr.data = td
        yield tr


@contextmanager
def remember_sql(model):
    SqlAsk.drop_collection()  # 清理下
    result = []
    with detect_state():
        yield result
    for sql in SqlAsk.objects():
        # 这些请求都是在这个请求发送的时候产生的
        sql_type, tables = parse(sql.rawcomment)
        for tb in tables:
            if tb not in model.aboutSql:
                model.aboutSql[tb] = {}
            if sql_type not in model.aboutSql[tb]:
                model.aboutSql[tb][sql_type] = []
            if sql.rawcomment not in model.aboutSql[tb][sql_type]:
                model.aboutSql[tb][sql_type].append(sql.rawcomment)
        result.append([sql.rawcomment, sql_type, json.dumps(tables)])
    model.save()


def remove_headers(item):
    item.headers = {}
    return item


def get_all_routes_and_values(all_asks):
    res = []
    all_asks = list(all_asks)
    if all_asks:
        model = APIModel.objects(url=all_asks[0].url).first()
        for route in model.args.get_sub_route():
            tr = (route, [])
            res.append(tr)
            for ask in all_asks:
                try:
                    tr[1].append(get_data_by_route(ask.data, route))
                except Exception:
                    pass
    return res


def summarize_param_type(route_value):
    from .constant import BaseTypeList
    key, values = route_value
    # 总结
    goal_type_i = 0
    for v in values:
        if not BaseTypeList[goal_type_i].check(v):
            goal_type_i += 1
    return key, str(BaseTypeList[goal_type_i]())


def verify_success(condition):
    def decorate(item):
        try:
            rt = json.loads(item.resp_content.decode())
            for key in condition:
                r = rt
                for k in key[:-1]:
                    r = r[k]
                if not condition[key](r[key[-1]]):
                    return False
            return True
        except:
            return False

    return decorate


def my_not(f):
    def decorate(*args, **kwargs):
        return not f(*args, **kwargs)

    return decorate


def my_all(f):
    def decorate(ite):
        for item in ite:
            if not f(item):
                return False
        return True

    return decorate


def resend(item):
    ask = copy(item)
    ask.resp_content = ask.send_again()
    ask.resp_content = ask.resp_content.content
    return ask


class Source:
    def __init__(self, **_filter):
        """
        计算起源
        :param _filter: 加速计算的筛选器 {key1 = key2}
        :return: 生成器函数
        """
        if _filter is None:
            self._filter = {}
        else:
            self._filter = _filter

        def f(model):
            tp_filter = {}
            for key in self._filter:
                tp_filter[key] = model[self._filter[key]]
            for item in Ask.objects(**tp_filter):
                yield item

        self.f = f

    def __call__(self, model):
        return self.f(model)

    def my_filter(self, condition):
        """
        筛选器
        :param condition: 筛选条件
        :return: 一个迭代器函数
        """
        f = self.f

        def decorate(model):
            history_mem = []
            for item in f(model):
                if condition.__code__.co_argcount == 1:
                    if condition(item):
                        yield item
                elif condition.__code__.co_argcount == 2:
                    if condition(item, history_mem):
                        yield item

        self.f = decorate
        return self

    def combine(self, *others):
        """
        粘合n个迭代器函数的结果
        :param f: 数个迭代器函数
        :return: 一个迭代器函数
        """
        f = self.f

        def decorate(model):
            for _f in others:
                for item in _f(model):
                    yield item
            for item in f(model):
                yield item

        self.f = decorate
        return self

    def IF(self, success=None, fail=None):
        """
        判断器，之前一个必须是 summary 作为判断条件
        :param success: 成功后走的流程
        :param fail: 失败后走的流程
        :return:
        """
        f = self.f

        def decorate(model):
            res = f(model)
            if res and success:
                yield from success(model)
            elif not res and fail:
                yield from fail(model)
            else:
                return []

        self.f = decorate
        return self

    def expand(self, generate):
        """
        拓展单个请求器
        :param generate: 生产器
        :return: 通道性返回
        """
        f = self.f

        def decorate(model):
            for item in f(model):
                yield from generate(item)

        self.f = decorate
        return self

    def update(self, worker):
        """
        更新器
        :param f: 迭代器函数
        :param worker:  加工器
        :return:
        """
        f = self.f

        def decorate(model):
            for item in f(model):
                yield worker(item)

        self.f = decorate
        return self

    def summary(self, func):
        """
        总结
        :param f:
        :param func: (iter) 返回一个值，根据问题类型会填写到相关的位置
        :return: 返回一个值，这个函数将会是整个工作流的终点
        """
        f = self.f

        def decorate(model):
            return func(f(model))

        self.f = decorate
        return self

    def gate(self, func):
        """
        闸门，拦下所有计算流，并全部输入到func当中处理后，重新放出
        :param func: (iter) 返回一个值，根据问题类型会填写到相关的位置
        :return: 返回一个流
        """
        f = self.f

        def decorate(model):
            yield from func(f(model))

        self.f = decorate
        return self

    def listen(self, context):
        f = self.f

        def decorate(model):
            ite = f(model)
            while True:
                try:
                    with context(model) as listen_result:
                        next(ite)
                except StopIteration:
                    break
                yield listen_result

        self.f = decorate
        return self

    def model_update(self, func):
        """
        :param func:修改model的函数
        :return:
        """
        f = self.f

        def decorate(model):
            yield from f(func(model))

        self.f = decorate
        return self


class Solution:
    def __init__(self):
        self.condition = lambda x: True
        self.cal_flow = None
        self.res_builder = None


@contextmanager
def solvers_register(problem_type, solvers):
    from .documents import QuestionState
    state = QuestionState.objects(que_type=problem_type).first()
    if not state:
        state = QuestionState()
        state.que_type = problem_type
    state.auto_solve_available = True
    state.save()
    s = Solution()
    yield s
    if problem_type not in solvers:
        solvers[problem_type] = []
    solvers[problem_type].append(s)
